﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using KZ.Exception;
using KZ.Lottery.Entities;
using KZ.Lottery.Resources;
using KZ.Utilities;

namespace KZ.Lottery.DAL
{
    public class ResultDAL
    {
        private static Result CreateResultFromReader(SqlDataReader reader)
        {
            Result result = new Result
            {
                ResultID = Utility.StringParse(reader[ResultColumns.ResultID.ToString()]),
                ResultDate = Utility.DateTimeParse(reader[ResultColumns.ResultDate.ToString()]),
                ChannelID = Utility.StringParse(reader[ResultColumns.ChannelID.ToString()]),
                ChannelName = Utility.StringParse(reader[ResultColumns.ChannelName.ToString()]),
                Head = Utility.StringParse(reader[ResultColumns.Head.ToString()]),
                Tail = Utility.StringParse(reader[ResultColumns.Tail.ToString()]),
                Status = Utility.StringParse(reader[ResultColumns.Status.ToString()])
            };

            result.StrStatus = KZ_Resource.ResourceManager.GetString(result.Status);
            result.Day = KZ_Resource.ResourceManager.GetString(result.ResultDate.DayOfWeek.ToString());

            return result;
        }

        public static Result GetResult(string resultID)
        {
            try
            {
                Result rule = new Result();

                DataAccessHelper.SqlCmd.Parameters.Add("ResultID", SqlDbType.NVarChar).Value = resultID;
                using (SqlDataReader reader = DataAccessHelper.ExecuteQuery(CommandType.StoredProcedure, KZ_StoreName.ResultGetByID))
                {
                    if (reader.Read())
                    {
                        rule = CreateResultFromReader(reader);
                    }
                }

                return rule;
            }
            catch (SqlException ex)
            {
                Log.LogEx("GetResult", ex);
                throw new DataAccessException(KZ_Message.BLM_ERR_RESULT_GET);
            }
        }

        public static List<Result> GetResultList(string channelID, DateTime from, DateTime to)
        {
            try
            {
                List<Result> resultList = new List<Result>();

                DataAccessHelper.SqlCmd.Parameters.Add("ChannelID", SqlDbType.NVarChar).Value = channelID;
                DataAccessHelper.SqlCmd.Parameters.Add("From", SqlDbType.Date).Value = from;
                DataAccessHelper.SqlCmd.Parameters.Add("To", SqlDbType.Date).Value = to;
                using (SqlDataReader reader = DataAccessHelper.ExecuteQuery(CommandType.StoredProcedure, KZ_StoreName.ResultGetList))
                {
                    while (reader.Read())
                    {
                        resultList.Add(CreateResultFromReader(reader));
                    }
                }

                return resultList;
            }
            catch (SqlException ex)
            {
                Log.LogEx("GetResultList", ex);
                throw new DataAccessException(KZ_Message.BLM_ERR_RESULT_GETLIST);
            }
        }

        public static bool IsExistResult(DateTime resultDate, string channelID)
        {
            try
            {
                DataAccessHelper.SqlCmd.Parameters.Add("ResultDate", SqlDbType.Date).Value = resultDate;
                DataAccessHelper.SqlCmd.Parameters.Add("ChannelID", SqlDbType.NVarChar).Value = channelID;
                return Utility.IntParse(DataAccessHelper.ExecuteScalar(CommandType.StoredProcedure, KZ_StoreName.ResultIsExist)) > 0;
            }
            catch (SqlException ex)
            {
                Log.LogEx("IsExistResult", ex);
                throw new DataAccessException(KZ_Message.BLM_ERR_RESULT_ISEXIST);
            }
        }

        public static bool DeleteResult(string resultID)
        {
            try
            {
                DataAccessHelper.SqlCmd.Parameters.Add("ResultID", SqlDbType.NVarChar).Value = resultID;
                if (DataAccessHelper.ExecuteNonQuery(CommandType.StoredProcedure, KZ_StoreName.ResultDelete) > 0)
                {
                    return true;
                }

                throw new DataAccessException(KZ_Message.BLM_ERR_RESULT_DELETE);
            }
            catch (SqlException ex)
            {
                Log.LogEx("DeleteResult", ex);
                throw new DataAccessException(KZ_Message.BLM_ERR_RESULT_DELETE);
            }
        }

        public static bool ApproveResult(List<Result> resultList)
        {
            try
            {
                decimal winRate = Utility.DecimalParse(SettingDAL.GetSetting(KZ_Resource.Setting_WinRate).SettingValue);
                decimal capitalRate = Utility.DecimalParse(SettingDAL.GetSetting(KZ_Resource.Setting_CapitalRate).SettingValue);
                List<SettingValue> settingValueList = SettingValueDAL.GetSettingValueList();
                int maxDay = settingValueList.Max(sv => sv.Day);
                List<GroupValue> groupValueList = GroupValueDAL.GetGroupValueList();

                RuleGroupDAL ruleGroupDAL = new RuleGroupDAL();
                RuleReportDAL ruleReportDAL = new RuleReportDAL();
                foreach (Result result in resultList)
                {
                    string ruleIDList = RuleDAL.GetRuleID(result.ResultDate.DayOfWeek.ToString(), result.ChannelID);
                    string[] ruleIDArray = ruleIDList.Replace("'", String.Empty).Split(',');
                    int numberOfRule = ruleIDArray.Length;
                    List<RuleGroup> ruleGroupList = RuleGroupDAL.GetRuleGroupList(ruleIDList, String.Empty, GroupColumns.Order.ToString(), "ASC");

                    foreach (string ruleID in ruleIDArray)
                    {
                        List<RuleGroup> ruleGroupTemp =
                            new List<RuleGroup>(ruleGroupList.Where(rg => rg.RuleID == ruleID));

                        DataAccessHelper.BeginTransaction(IsolationLevel.Serializable);
                        try
                        {
                            decimal headWinAmount = 0;
                            decimal tailWinAmount = 0;
                            decimal headTotalAmount = 0;
                            decimal tailTotalAmount = 0;
                            decimal headFirstDayAmount = 0;
                            decimal tailFirstDayAmount = 0;

                            foreach (RuleGroup item in ruleGroupTemp)
                            {
                                //// compute amount (minus for first day)
                                if (item.Type == KZ_Resource.Head)
                                {
                                    headTotalAmount += settingValueList[item.Day - 1].Value;
                                    headFirstDayAmount += item.FirstDayAmount;
                                }
                                else
                                {
                                    tailTotalAmount += settingValueList[item.Day - 1].Value;
                                    tailFirstDayAmount += item.FirstDayAmount;
                                }

                                //// group win lottery
                                //// reset to first day
                                if (groupValueList.Exists(gv =>
                                    gv.GroupID == item.GroupID &&
                                    gv.Value == result.Head && item.Type == KZ_Resource.Head))
                                {
                                    headWinAmount += settingValueList[item.Day - 1].Value - item.FirstDayAmount;
                                    item.Day = 1;
                                }
                                else if (groupValueList.Exists(gv =>
                                    gv.GroupID == item.GroupID &&
                                    gv.Value == result.Tail && item.Type == KZ_Resource.Tail))
                                {
                                    tailWinAmount += settingValueList[item.Day - 1].Value - item.FirstDayAmount;
                                    item.Day = 1;
                                }
                                else //// group not win lottery
                                {    //// inrease 1 day, if day is max => reset to first day
                                    item.Day = item.Day + 1 > maxDay ? 1 : item.Day + 1;
                                }

                                ruleGroupDAL.UpdateRuleGroup(item);
                            }

                            decimal headCapital = (headTotalAmount - headFirstDayAmount) * Global.NumberOfGroup;
                            decimal tailCapital = (tailTotalAmount - tailFirstDayAmount) * Global.NumberOfGroup;
                            decimal headActualCapital = headCapital * capitalRate / 100;
                            decimal tailActualCapital = tailCapital * capitalRate / 100;
                            headWinAmount *= winRate;
                            tailWinAmount *= winRate;

                            RuleDAL.UpdateRuleAmount(ruleID, headWinAmount, tailWinAmount,
                                headCapital, tailCapital, headActualCapital, tailActualCapital);
                            UpdateResultStatus(result.ResultID);

                            RuleReport ruleReport = new RuleReport
                            {
                                Date = result.ResultDate,
                                RuleID = ruleID,
                                ChannelID = result.ChannelID,
                                HeadCapital = headCapital,
                                TailCapital = tailCapital,
                                HeadActualCapital = headActualCapital,
                                TailActualCapital = tailActualCapital,
                                HeadWinAmount = headWinAmount,
                                TailWinAmount = tailWinAmount,
                            };
                            ruleReportDAL.InsertRuleReport(ruleReport);

                            DataAccessHelper.CommitTransaction();
                        }
                        catch (DataAccessException)
                        {
                            DataAccessHelper.RollbackTransaction();
                            throw;
                        }
                    }
                }

                return true;
            }
            catch (DataAccessException)
            {
                throw;
            }
        }

        private static bool UpdateResultStatus(string resultID)
        {
            try
            {
                DataAccessHelper.SqlCmd.Parameters.Add("ResultID", SqlDbType.NVarChar).Value = resultID;
                if (DataAccessHelper.ExecuteNonQueryWithTransaction(CommandType.StoredProcedure, KZ_StoreName.ResultUpdateStatus) > 0)
                {
                    return true;
                }

                throw new DataAccessException(KZ_Message.BLM_ERR_RESULT_UPDATE);
            }
            catch (SqlException ex)
            {
                Log.LogEx("UpdateResultStatus", ex);
                throw new DataAccessException(KZ_Message.BLM_ERR_RESULT_UPDATE);
            }
        }

        public bool InsertResult(Result result)
        {
            try
            {
                DataAccessHelper.SqlCmd.Parameters.Add("ResultID", SqlDbType.NVarChar).Value = result.ResultID;
                DataAccessHelper.SqlCmd.Parameters.Add("ResultDate", SqlDbType.Date).Value = result.ResultDate;
                DataAccessHelper.SqlCmd.Parameters.Add("ChannelID", SqlDbType.NVarChar).Value = result.ChannelID;
                DataAccessHelper.SqlCmd.Parameters.Add("Head", SqlDbType.NVarChar).Value = result.Head;
                DataAccessHelper.SqlCmd.Parameters.Add("Tail", SqlDbType.NVarChar).Value = result.Tail;
                DataAccessHelper.SqlCmd.Parameters.Add("Status", SqlDbType.NVarChar).Value = result.Status;
                if (DataAccessHelper.ExecuteNonQuery(CommandType.StoredProcedure, KZ_StoreName.ResultInsert) > 0)
                {
                    return true;
                }

                throw new DataAccessException(KZ_Message.BLM_ERR_RESULT_CHANNEL_EXIST);
            }
            catch (SqlException ex)
            {
                Log.LogEx("InsertResult", ex);
                throw new DataAccessException(KZ_Message.BLM_ERR_RESULT_INSERT);
            }
        }

        public bool UpdateResult(Result result)
        {
            try
            {
                DataAccessHelper.SqlCmd.Parameters.Add("ResultID", SqlDbType.NVarChar).Value = result.ResultID;
                DataAccessHelper.SqlCmd.Parameters.Add("ResultDate", SqlDbType.Date).Value = result.ResultDate;
                DataAccessHelper.SqlCmd.Parameters.Add("ChannelID", SqlDbType.NVarChar).Value = result.ChannelID;
                DataAccessHelper.SqlCmd.Parameters.Add("Head", SqlDbType.NVarChar).Value = result.Head;
                DataAccessHelper.SqlCmd.Parameters.Add("Tail", SqlDbType.NVarChar).Value = result.Tail;
                if (DataAccessHelper.ExecuteNonQuery(CommandType.StoredProcedure, KZ_StoreName.ResultUpdate) > 0)
                {
                    return true;
                }

                throw new DataAccessException(KZ_Message.BLM_ERR_RESULT_CHANNEL_EXIST);
            }
            catch (SqlException ex)
            {
                Log.LogEx("UpdateResult", ex);
                throw new DataAccessException(KZ_Message.BLM_ERR_RESULT_UPDATE);
            }
        }
    }
}